package main

import (
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
)

type volumn struct {
	Src        string    `json:"src"`
	Dst        string    `json:"dst"`
	MountPoint mountInfo `json:"mount_point"`
}

func parseVolumn(value string) volumn {
	tmp := strings.SplitN(value, ":", 2)
	var err error
	tmp[0], err = filepath.Abs(tmp[0])
	assert(err)
	if len(tmp) < 1 {
		tmp = append(tmp, tmp[0])
	}
	return volumn{
		Src: tmp[0],
		Dst: tmp[1],
	}
}

func (v volumn) String() string {
	return v.Src + ":" + v.Dst
}

func parseVolumnArgs(volumns []string) ([]volumn, map[int]mountInfo) {
	var vols []volumn
	mounts := make(map[int]mountInfo)
	for _, vol := range volumns {
		v := parseVolumn(vol)
		fs := syscall.Stat_t{}
		assert(syscall.Stat(v.Src, &fs))
		info, err := getMountInfo(uint16(fs.Dev))
		assert(err)
		if DEBUG {
			fmt.Printf("[DEBUG]Mount %s:\n", v.Src)
			fmt.Printf("[DEBUG]  mount_point=%s, stdev=%s\n", info.Source, info.dev)
		}
		v.MountPoint = info
		vols = append(vols, v)
		mounts[info.ID] = info
	}
	return vols, mounts
}

func mountVolumns(mounts []mountInfo, volumns []volumn) {
	var del []string
	var umount []string
	// mount host devs
	for _, dev := range mounts {
		os.Remove(dev.Source)
		if _, err := os.Stat(path.Dir(dev.Source)); os.IsNotExist(err) {
			assert(os.MkdirAll(path.Dir(dev.Source), 0755))
			del = append(del, path.Dir(dev.Source))
		}
		assert(syscall.Mknod(dev.Source, syscall.S_IFBLK, int(dev.StDev)))
		mountDir := path.Join(os.TempDir(), "_rootfs_", dev.Mount)
		assert(os.MkdirAll(mountDir, 0755))
		assert(syscall.Mount(dev.Source, mountDir, dev.FileSystem, 0, ""))
		umount = append(umount, mountDir)
		if DEBUG {
			fmt.Printf("Mount host rootfs %s in %s\n", dev.Source, mountDir)
		}
	}
	// mount files or dir
	for _, vol := range volumns {
		srcDir := path.Join(os.TempDir(), "_rootfs_", vol.MountPoint.Mount, vol.Src)
		assert(syscall.Mount(srcDir, vol.Dst, "", syscall.MS_BIND, ""))
		if DEBUG {
			fmt.Printf("Mount %s in %s\n", srcDir, vol.Dst)
		}
	}
	// unmount host devs
	for _, dir := range umount {
		assert(syscall.Unmount(dir, 0))
	}
	// remove mount dirs
	for _, dir := range del {
		assert(os.RemoveAll(dir))
	}
	// remove _rootfs_ dir
	os.RemoveAll(path.Join(os.TempDir(), "_rootfs_"))
}
